package com.knowswift.issues.mqtt;

import cn.hutool.core.bean.BeanUtil;
import com.knowswift.issues.mqtt.message.MessageObject;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class MqttMessageUtil {
    public static Integer byteToInt(Byte b) {
        return (int) b;
    }

    public static Byte intToByte(Integer i) {
        return i.byteValue();
    }

    public static Integer byteToUnsignedInt(Byte b) {
        return Byte.toUnsignedInt(b);
    }

    public static Integer twoBytesToUnsignedInt(byte b1, byte b2) {
        return ((b1 & 0xFF) << 8) | (b2 & 0xFF);
    }

    public static Integer twoBytesToUnsignedInt(byte[] bytes, Integer offset) {
        return twoBytesToUnsignedInt(bytes[offset], bytes[offset + 1]);
    }


    public static Integer twoBytesToInt(byte b1, byte b2) {
        return (((b1 & 0xFF) << 24) | ((b2 & 0xFF) << 16)) >> 16;
    }

    public static Integer twoBytesToInt(byte[] bytes, Integer offset) {
        return twoBytesToInt(bytes[offset], bytes[offset + 1]);
    }

    public static byte[] intToTwoBytes(Integer i) {
        byte[] bytes = new byte[2];
        bytes[0] = intToByte(i >> 8);// 高位优先
        bytes[1] = intToByte(i);
        return bytes;
    }


    public static byte[] intToBytes(Integer i) {
        byte[] bytes = new byte[4];
        bytes[0] = (byte) ((i >> 24) & 0xFF);
        bytes[1] = (byte) ((i >> 16) & 0xFF);
        bytes[2] = (byte) ((i >> 8) & 0xFF);
        bytes[3] = (byte) (i & 0xFF);
        return bytes;
    }

    public static void writeIntToBytes(Integer value, Integer length, Integer offset, byte[] bytes) {
        byte[] temp = intToBytes(value, length);
        for (byte b : temp) {
            bytes[offset++] = b;
        }
    }

//    public static Integer bytesToInt(byte[] bytes){
//
//    }

    public static Integer bytesToInt(byte b1, byte b2, byte b3, byte b4) {
        return ((b1 & 0xFF) << 24) |
                ((b2 & 0xFF) << 16) |
                ((b3 & 0xFF) << 8) |
                ((b4 & 0xFF));
    }


    public static Integer bytesToInt(byte[] bytes, int offset, int length) {
        int result = (bytes[offset++] & 0xFF) << (8 * (length - 1));
        for (int i = length - 2; i >= 0; i--) {
            result = result | (bytes[offset++] & 0xFF) << (8 * i);
        }
        return result;
    }

    public static byte[] intToBytes(Integer i, Integer length) {
        byte[] bytes = new byte[length];
        for (int j = 0; j < length; j++) {
            bytes[j] = (byte) ((i >> (length - j - 1) * 8) & 0xFF);
        }
        return bytes;
    }

    public static <T> T readObject(byte[] bytes, Class<T> clazz) {
        Field[] declaredFields = clazz.getSuperclass().getDeclaredFields();
        declaredFields = ArrayUtils.addAll(declaredFields, clazz.getDeclaredFields());
        Map<String, Integer> temp = new HashMap<>();
        int offset = 0;
        for (Field declaredField : declaredFields) {
            DataLength annotation = declaredField.getAnnotation(DataLength.class);
            int dataLength = getDataLength(temp, annotation);
            Integer value = bytesToInt(bytes, offset, dataLength);
            offset += dataLength;
            temp.put(declaredField.getName(), value);
        }

        return BeanUtil.mapToBean(temp, clazz, false);
    }

    private static Integer getDataLength(Map<String, Integer> map, DataLength annotation) {
        int dataLength = 1;
        if (annotation != null) {
            dataLength = annotation.length();
            if (StringUtils.isNotBlank(annotation.refer())) {
                dataLength = map.get(annotation.refer());
            }
        }
        return dataLength;
    }

    public static byte[] objectToBytes(MessageObject messageObject) throws IllegalAccessException, NoSuchFieldException {
        byte[] bytes = new byte[messageObject.getLength()];
        Field[] declaredFields = messageObject.getClass().getSuperclass().getDeclaredFields();
        declaredFields = ArrayUtils.addAll(declaredFields, messageObject.getClass().getDeclaredFields());
        int offset = 0;
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            DataLength annotation = declaredField.getAnnotation(DataLength.class);
            int dataLength = getDataLength(messageObject, annotation);
            writeIntToBytes((Integer) declaredField.get(messageObject), dataLength, offset, bytes);
            offset += dataLength;
        }
        return bytes;
    }

    private static Integer getDataLength(Object object, DataLength annotation) throws NoSuchFieldException, IllegalAccessException {
        int dataLength = 1;
        if (annotation != null) {
            dataLength = annotation.length();
            if (StringUtils.isNotBlank(annotation.refer())) {
                dataLength = object.getClass().getDeclaredField(annotation.refer()).getInt(object);
            }
        }
        return dataLength;
    }

    public static String getSerialNumberFromTopic(String topic) {
        String[] split = topic.split("/");
        return split[3];
    }


}

